Developing a Stratified Sampling Scheme for the Unmonitored Systems Using K-means Clustering Method

Background

The aim of this analysis is to analyze SMP population using CWL monitoring data, categorize it, and identify underrepresented SMP features in our data set. We will also use clustering to group systems with similar characteristics and develop a stratified sampling script to randomly select a representative sample of systems proportional to their cluster and system type. This will ensure a comprehensive data set covering all types of SMPs with varying design metrics such as loading ratios, drainage area, and storm size management. The output of this analysis will help the data collection team with their future site selection.

  1. Finding the over-represented/under-represented SMPs
#Libraries
      library(odbc)
      library(DBI)
      library(lubridate)
      library(tidyverse)
      library(stats)
      library(gridExtra)
      library(grid)
      library(gtable)
      library(ggtext)
      library(dplyr)
      library(ggplot2)
      library(knitr)
      library(plotly)
      library(cluster)
      library(factoextra)
# DB PG14
    con <- dbConnect(odbc::odbc(), dsn = "mars14_data", uid = Sys.getenv("shiny_uid"), pwd = Sys.getenv("shiny_pwd"), MaxLongVarcharSize = 8190)

#Getting list of SMPs
    cwl_smp <- dbGetQuery(con,"SELECT DISTINCT smp_id
           FROM fieldwork.viw_deployment_full_cwl")
    
#Getting the greenit info
    smpbdv <- dbGetQuery(con,"SELECT * FROM external.tbl_smpbdv")
    sysbdv <- dbGetQuery(con,"SELECT * FROM external.tbl_systembdv")
    greenit_unified <-dbGetQuery(con,"SELECT * FROM external.viw_greenit_unified")

# join the greenit table with our smp list
    smp_features <- cwl_smp %>% 
      inner_join(smpbdv, by="smp_id")
# Unmonitored SMPs
    unmonitored_smp <- read.csv("C:\\Users\\Farshad.Ebrahimi\\OneDrive - City of Philadelphia\\Github Projects\\smp-population-assesment\\unmonitored_sites.csv") %>%
      select(smp_id = SMP.ID) %>%
      distinct()
# unmonitored smp_features
    unmonitored_smp_features <- unmonitored_smp %>%
      inner_join(smpbdv, by="smp_id")

The code calculates the distribution of SMP types with CWL data in the MARS database, providing an initial understanding of the distribution of SMP subtypes.

#number of smp types
smp_type_break <- smp_features %>% 
  group_by(smp_smptype) %>%
  summarise(count = n()) %>%
  select(Type = smp_smptype, count)

#number of unmonitored smps
unmonitored_smp_type_break <- unmonitored_smp_features %>% 
  group_by(smp_smptype) %>%
  summarise(count = n()) %>%
  select(Type = smp_smptype, count)

Similar analysis is performed below to calculate the distribution of SMP types in the entire population of SMP including the monitored and unmonitored SMPs.

The analysis aims to determine the over-represented (normalized percent > 1) and under-represented (normalized percent < 1) SMP types by dividing the fraction of a specific SMP type in the monitored data by the fraction in the entire population.

#combining all smps (monitored and unmonitored) and get fractions based on type of smp-finally normalize them
total_smp <- unmonitored_smp_type_break %>%
  full_join(smp_type_break, by="Type")
total_smp[is.na(total_smp)] <- 0
total_smp <- total_smp %>%
  mutate(total_number = count.x+count.y) %>%
  select(Type, total_number)
total_smp <- total_smp %>%
    mutate(sum_all = sum(total_number)) %>%
    mutate(percent_all = (total_number/sum_all)*100)

#get the percent of monitored SMP
smp_type_break_fraction <- smp_type_break %>%
    mutate(sum_all = sum(count)) %>%
    mutate(percent_monitored = (count/sum_all)*100)

#normalize the monitored smp by the total smp percents
normalized_fractions <- smp_type_break_fraction %>%
  full_join(total_smp, by="Type") %>%
  mutate(normalized_percent = percent_monitored/percent_all) %>%
  select(Type, percent_monitored, percent_all, normalized_percent)

normalized_fractions[is.na(normalized_fractions)] <- 0
#calculating the normalized percentage of smp types
kable(arrange(normalized_fractions, normalized_percent), caption = "Normalized SMP Break-down")
Normalized SMP Break-down
Type percent_monitored percent_all normalized_percent
Green Roof 0.0000000 0.1490313 0.0000000
Stormwater Tree 0.0000000 4.1728763 0.0000000
Bumpout 2.8017241 8.5692996 0.3269490
Swale 1.2931034 2.5335320 0.5103955
Planter 5.8189655 7.5260805 0.7731734
Infiltration/Storage Trench 25.8620690 29.4336811 0.8786556
Basin 0.2155172 0.2235469 0.9640805
Rain Garden 12.0689655 11.1028316 1.0870169
Tree Trench 50.6465517 35.6929955 1.4189493
Pervious Paving 0.4310345 0.2980626 1.4461207
Drainage Well 0.8620690 0.2980626 2.8922414

The table above shows Tree trenches are over-represented in our CWL data, while bumpout, swale and planters are under-represented (due to monitoring limitation, etc). However, it should be noted that MARS does not plan on monitoring greef roofs due to limitations on access to these systems and a 1:1 loading ratios. Stromwater trees, smilarly, does not have any CWL data and the monitoring efforts are limited to CET tests only. Porous pavements are also limited to infiltration rate testing and have no CWL data.

  1. Clustering at the system-level

The current section aims to categorize the systems within each broad subtypes. The reason to perform this at the system level is the accessibility of design metrics at the system level. The clustering within each type of systems is done to ensure the sampling approach picks an appropriate number of systems from systems possessing similar characteristics within these types. For example, the sampling method should select a larger number of Bioinfiltration systems with higher drainage area if those systems constitute the majority of Bioinfiltration systems.

The binning approach considers several system features like loading ratios, drainage area, and storm size management. The code chunks below demonstrate a clustering method applied to unmonitored systems, regardless of subtypes, as an example of how it can group systems based solely on these three design metrics. The clustering will then be repeated within each subtype to provide a more relevant method for sampling.

#rawstormsizemanaged-in , sys_impervda_ft2, and loading ratio metrics gathering

table_all <- unmonitored_smp_features %>%
  left_join(sysbdv, by="system_id") %>%
  mutate(loading_ratio = sys_lrtotalda_ft2 )

K-means clustering is an unsupervised learning method that groups data points based on their proximity to centroids, which are calculated as the mean of all points in a cluster. The algorithm starts by randomly selecting initial centroids, and then repeatedly adjusts them until convergence is achieved

The optimal number of clusters is determined using the Elbow method. In summary, this method picks the number of clusters in which a the slope of the lines changes more drastically (cluster 4 in the following graph).

#Clustring analysis based on 3 features of drainage area, loading ratio, and storm sized managed

clustered_moniored <- table_all %>%
  select(system_id, sys_impervda_ft2, loading_ratio, sys_rawstormsizemanaged_in) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_moniored
normalized_cluster[,2:4] <- scale(clustered_moniored[,2:4])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 4)

# Store the cluster assignments back into the clustering data frame object
clustered_moniored$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_moniored$cluster)

  1   2   3   4 
 21 279 136  11 

The above table displays the number of systems in each cluster. The subsequent tables aim to determine the average of system design metrics in each category. The data can be analyzed by examining each category; for instance, Cluster Four encompasses systems with a large drainage area, capable of handling larger storm sizes (>3 inch/hr).

# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_moniored %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 loading_ratio sys_rawstormsizemanaged_in
1 17695.05 68.36332 1.491118
2 20363.52 12.75067 1.886491
3 17350.55 23.52892 1.212609
4 62204.82 10.56821 3.843215
ggplot(data = clustered_moniored, aes(x = loading_ratio, y = sys_rawstormsizemanaged_in, color = cluster)) +
    geom_point()

plot_ly(clustered_moniored, x = ~sys_impervda_ft2, y = ~loading_ratio, z = ~sys_rawstormsizemanaged_in) %>%
  add_markers(color = ~cluster)

In order to create a more pertinent clustering method that distinguishes between general types of systems (lined vs. unlined or bioretention vs. bioinfiltration), the unmonitored systems were divided into broad categories, including Bioinfiltration, Bioretention (lined), Bioretention (unlined), Subsurface Infiltration, Subsurface Slow Release (lined), and Subsurface Slow Release (unlined) using the system-level feature called sys_modelinputcategory. The code chunk below displays a breakdown of these SMPs. The clustering algorithm was repeated for each of those groups similarly. Please note that Green Roofs and Permeable Pavements were not included due to a low number of systems.

#sys_modelinputcategory categories
sys_cat <- table_all %>%
  select(system_id, sys_modelinputcategory)%>%
  distinct %>%
  group_by(sys_modelinputcategory) %>%
  summarise(count = n())

kable(sys_cat)
sys_modelinputcategory count
Bioinfiltration 124
Bioretention (lined) 29
Bioretention (unlined) 83
Green Roof 2
Permeable Pavement 1
Subsurface infiltration 159
Subsurface slow release (lined) 113
Subsurface slow release (unlined) 113

-Bioinfiltration Clustering:


clustered_Bioinfiltration <- table_all %>%
  filter(sys_modelinputcategory == "Bioinfiltration") %>%
  select(system_id, sys_impervda_ft2, loading_ratio, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioinfiltration
normalized_cluster[,2:4] <- scale(clustered_Bioinfiltration[,2:4])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 4)

# Store the cluster assignments back into the clustering data frame object
clustered_Bioinfiltration$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_Bioinfiltration$cluster)

 1  2  3  4 
17 31 72  4 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioinfiltration %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 loading_ratio sys_rawstormsizemanaged_in
1 11184.24 40.58019 1.063568
2 51515.00 15.79839 1.752800
3 15718.52 12.39893 1.778769
4 22358.50 10.18006 4.348037

#3 D plot
plot_ly(clustered_Bioinfiltration, x = ~sys_impervda_ft2, y = ~loading_ratio, z = ~sys_rawstormsizemanaged_in) %>%
  add_markers(color = ~cluster)

-Bioretention (lined) clustering. Note that loading ratio field is not applicable here:


clustered_Bioreten_lined <- table_all %>%
  filter(sys_modelinputcategory == "Bioretention (lined)") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioreten_lined
normalized_cluster[,2:3] <- scale(clustered_Bioreten_lined[,2:3])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)

# Store the cluster assignments back into the clustering data frame object
clustered_Bioreten_lined$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_Bioreten_lined$cluster)

 1  2 
 5 24 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioreten_lined %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in
1 73919.40 2.270016
2 18291.83 1.473926

#3 D plot
plot_ly(clustered_Bioreten_lined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in) %>%
  add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

-Bioretention (unlined) clustering:


clustered_Bioreten_unlined <- table_all %>%
  filter(sys_modelinputcategory == "Bioretention (unlined)") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioreten_unlined
normalized_cluster[,2:4] <- scale(clustered_Bioreten_unlined[,2:4])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 3)

# Store the cluster assignments back into the clustering data frame object
clustered_Bioreten_unlined$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_Bioreten_unlined$cluster)

 1  2  3 
 9 21 53 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioreten_unlined %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in loading_ratio
1 7046.333 3.309078 4.263439
2 42812.095 1.525561 20.624402
3 16789.575 1.639162 12.959101

#3 D plot
plot_ly(clustered_Bioreten_unlined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z= ~loading_ratio) %>%
  add_markers(color = ~cluster)

-Subsurface infiltration clustering:


clustered_subsurface <- table_all %>%
  filter(sys_modelinputcategory == "Subsurface infiltration") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_subsurface
normalized_cluster[,2:4] <- scale(clustered_subsurface[,2:4])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 2)

# Store the cluster assignments back into the clustering data frame object
clustered_subsurface$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_subsurface$cluster)

 1  2 
99 26 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_subsurface %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in loading_ratio
1 19525.382 1.837962 12.69806
2 7850.692 1.030724 31.54683

#3 D plot
plot_ly(clustered_subsurface, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z= ~loading_ratio) %>%
  add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

-Subsurface slow release (lined) clustering:


clustered_slowrel_lined <- table_all %>%
  filter(sys_modelinputcategory == "Subsurface slow release (lined)") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_slowrel_lined
normalized_cluster[,2:3] <- scale(clustered_slowrel_lined[,2:3])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)

# Store the cluster assignments back into the clustering data frame object
clustered_slowrel_lined$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_slowrel_lined$cluster)

 1  2 
58 55 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_slowrel_lined %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in
1 13139.92 1.195020
2 20312.98 1.720648

#3 D plot
plot_ly(clustered_slowrel_lined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in) %>%
  add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

-Subsurface slow release (unlined) clustering:


clustered_slowrel_unlined <- table_all %>%
  filter(sys_modelinputcategory == "Subsurface slow release (unlined)") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_slowrel_unlined
normalized_cluster[,2:3] <- scale(clustered_slowrel_unlined[,2:3])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)

# Store the cluster assignments back into the clustering data frame object
clustered_slowrel_unlined$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_slowrel_unlined$cluster)

 1  2 
61 52 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_slowrel_unlined %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in loading_ratio
1 18886.52 1.910538 19.26955
2 16819.60 1.337858 34.30980

#3 D plot
plot_ly(clustered_slowrel_unlined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z = ~loading_ratio) %>%
  add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Upon clustering the systems, the data will be collected in a unified data frame for stratified sampling. The stratified sampling creates a balanced list of systems that are reflective of the size of each system type and that of the internal clusters. For example, the code below has generated 58 system (~ 10% of total unmonitored systems); the size of each system type is proportional to that of the population, and within each type of systems, a proportional number of systems has been chosen from each cluster e.g., in Bioinfiltration type systems, 7 systems are randomly chosen from cluster 3, 3 systems from cluster 2, and 2 systems from cluster 1.

#binding all sub-type groups into one table
sys_clustered_all <- bind_rows(clustered_Bioinfiltration[,c("system_id","cluster","sys_modelinputcategory")], clustered_Bioreten_lined[,c("system_id","cluster","sys_modelinputcategory")], clustered_Bioreten_unlined[,c("system_id","cluster","sys_modelinputcategory")], clustered_slowrel_lined[,c("system_id","cluster","sys_modelinputcategory")], clustered_slowrel_unlined[,c("system_id","cluster","sys_modelinputcategory")], clustered_subsurface[,c("system_id","cluster","sys_modelinputcategory")])

#sampling code
stratified_sample <- sys_clustered_all %>%
    group_by(sys_modelinputcategory, cluster) %>%
    sample_frac(size=0.1)

kable(stratified_sample)
system_id cluster sys_modelinputcategory
1023-11 1 Bioinfiltration
443-1 1 Bioinfiltration
1083-6 2 Bioinfiltration
1127-1 2 Bioinfiltration
1195-3 2 Bioinfiltration
991-1 3 Bioinfiltration
310-6 3 Bioinfiltration
410-2 3 Bioinfiltration
1315-1 3 Bioinfiltration
1140-2 3 Bioinfiltration
244-1 3 Bioinfiltration
416-1 3 Bioinfiltration
1007-2 2 Bioretention (lined)
1067-4 2 Bioretention (lined)
1178-6 1 Bioretention (unlined)
1273-6 2 Bioretention (unlined)
578-2 2 Bioretention (unlined)
595-4 3 Bioretention (unlined)
596-4 3 Bioretention (unlined)
1265-3 3 Bioretention (unlined)
1200-3 3 Bioretention (unlined)
588-2 3 Bioretention (unlined)
443-3 1 Subsurface infiltration
1051-15 1 Subsurface infiltration
280-3 1 Subsurface infiltration
344-1 1 Subsurface infiltration
1070-6 1 Subsurface infiltration
1281-27 1 Subsurface infiltration
637-1 1 Subsurface infiltration
1010-5 1 Subsurface infiltration
1242-1 1 Subsurface infiltration
1138-1 1 Subsurface infiltration
441-13 2 Subsurface infiltration
1298-1 2 Subsurface infiltration
441-12 2 Subsurface infiltration
1059-3 1 Subsurface slow release (lined)
1240-2 1 Subsurface slow release (lined)
1202-2 1 Subsurface slow release (lined)
525-1 1 Subsurface slow release (lined)
554-7 1 Subsurface slow release (lined)
994-3 1 Subsurface slow release (lined)
634-1 2 Subsurface slow release (lined)
1011-3 2 Subsurface slow release (lined)
1267-2 2 Subsurface slow release (lined)
1206-2 2 Subsurface slow release (lined)
1129-4 2 Subsurface slow release (lined)
1287-6 2 Subsurface slow release (lined)
1051-17 1 Subsurface slow release (unlined)
595-10 1 Subsurface slow release (unlined)
1209-6 1 Subsurface slow release (unlined)
1275-2 1 Subsurface slow release (unlined)
1062-5 1 Subsurface slow release (unlined)
1137-6 1 Subsurface slow release (unlined)
1070-11 2 Subsurface slow release (unlined)
1265-11 2 Subsurface slow release (unlined)
989-1 2 Subsurface slow release (unlined)
1359-2 2 Subsurface slow release (unlined)
1051-3 2 Subsurface slow release (unlined)

To generate monitoring systems, we developed a semi-random approach that utilizes a binning technique. This approach involves assigning systems to clusters based on their specific type, with the binning process utilizing a combination of design metrics to group similar systems together. To ensure a representative sample of systems, the random selection process chooses a proportional number of systems from each cluster and system type based on their size.

LS0tDQp0aXRsZTogIlNNUCBQb3B1bGF0aW9uIEFzc2VzbWVudCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KYXV0aG9yOiAiRmFyc2hhZCBFYnJhaGltaSwgRmViLzcvMjAyMyINCi0tLQ0KDQoqKkRldmVsb3BpbmcgYSBTdHJhdGlmaWVkIFNhbXBsaW5nIFNjaGVtZSBmb3IgdGhlIFVubW9uaXRvcmVkIFN5c3RlbXMgVXNpbmcgSy1tZWFucyBDbHVzdGVyaW5nIE1ldGhvZCoqDQoNCioqQmFja2dyb3VuZCoqDQoNClRoZSBhaW0gb2YgdGhpcyBhbmFseXNpcyBpcyB0byBhbmFseXplIFNNUCBwb3B1bGF0aW9uIHVzaW5nIENXTCBtb25pdG9yaW5nIGRhdGEsIGNhdGVnb3JpemUgaXQsIGFuZCBpZGVudGlmeSB1bmRlcnJlcHJlc2VudGVkIFNNUCBmZWF0dXJlcyBpbiBvdXIgZGF0YSBzZXQuIFdlIHdpbGwgYWxzbyB1c2UgY2x1c3RlcmluZyB0byBncm91cCBzeXN0ZW1zIHdpdGggc2ltaWxhciBjaGFyYWN0ZXJpc3RpY3MgYW5kIGRldmVsb3AgYSBzdHJhdGlmaWVkIHNhbXBsaW5nIHNjcmlwdCB0byByYW5kb21seSBzZWxlY3QgYSByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2Ygc3lzdGVtcyBwcm9wb3J0aW9uYWwgdG8gdGhlaXIgY2x1c3RlciBhbmQgc3lzdGVtIHR5cGUuIFRoaXMgd2lsbCBlbnN1cmUgYSBjb21wcmVoZW5zaXZlIGRhdGEgc2V0IGNvdmVyaW5nIGFsbCB0eXBlcyBvZiBTTVBzIHdpdGggdmFyeWluZyBkZXNpZ24gbWV0cmljcyBzdWNoIGFzIGxvYWRpbmcgcmF0aW9zLCBkcmFpbmFnZSBhcmVhLCBhbmQgc3Rvcm0gc2l6ZSBtYW5hZ2VtZW50LiBUaGUgb3V0cHV0IG9mIHRoaXMgYW5hbHlzaXMgd2lsbCBoZWxwIHRoZSBkYXRhIGNvbGxlY3Rpb24gdGVhbSB3aXRoIHRoZWlyIGZ1dHVyZSBzaXRlIHNlbGVjdGlvbi4NCg0KMS4gICoqRmluZGluZyB0aGUgb3Zlci1yZXByZXNlbnRlZC91bmRlci1yZXByZXNlbnRlZCBTTVBzKioNCg0KYGBge3Igc2VjdGlvbiAxLCBMb2FkaW5nIGxpYnJhcmllcyBhbmQgcXVlcnlpbmcgc21wIGRhdGEgfQ0KI0xpYnJhcmllcw0KICAgICAgbGlicmFyeShvZGJjKQ0KICAgICAgbGlicmFyeShEQkkpDQogICAgICBsaWJyYXJ5KGx1YnJpZGF0ZSkNCiAgICAgIGxpYnJhcnkodGlkeXZlcnNlKQ0KICAgICAgbGlicmFyeShzdGF0cykNCiAgICAgIGxpYnJhcnkoZ3JpZEV4dHJhKQ0KICAgICAgbGlicmFyeShncmlkKQ0KICAgICAgbGlicmFyeShndGFibGUpDQogICAgICBsaWJyYXJ5KGdndGV4dCkNCiAgICAgIGxpYnJhcnkoZHBseXIpDQogICAgICBsaWJyYXJ5KGdncGxvdDIpDQogICAgICBsaWJyYXJ5KGtuaXRyKQ0KICAgICAgbGlicmFyeShwbG90bHkpDQogICAgICBsaWJyYXJ5KGNsdXN0ZXIpDQogICAgICBsaWJyYXJ5KGZhY3RvZXh0cmEpDQojIERCIFBHMTQNCiAgICBjb24gPC0gZGJDb25uZWN0KG9kYmM6Om9kYmMoKSwgZHNuID0gIm1hcnMxNF9kYXRhIiwgdWlkID0gU3lzLmdldGVudigic2hpbnlfdWlkIiksIHB3ZCA9IFN5cy5nZXRlbnYoInNoaW55X3B3ZCIpLCBNYXhMb25nVmFyY2hhclNpemUgPSA4MTkwKQ0KDQojR2V0dGluZyBsaXN0IG9mIFNNUHMNCiAgICBjd2xfc21wIDwtIGRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgRElTVElOQ1Qgc21wX2lkDQogICAgICAgICAgIEZST00gZmllbGR3b3JrLnZpd19kZXBsb3ltZW50X2Z1bGxfY3dsIikNCiAgICANCiNHZXR0aW5nIHRoZSBncmVlbml0IGluZm8NCiAgICBzbXBiZHYgPC0gZGJHZXRRdWVyeShjb24sIlNFTEVDVCAqIEZST00gZXh0ZXJuYWwudGJsX3NtcGJkdiIpDQogICAgc3lzYmR2IDwtIGRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgKiBGUk9NIGV4dGVybmFsLnRibF9zeXN0ZW1iZHYiKQ0KICAgIGdyZWVuaXRfdW5pZmllZCA8LWRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgKiBGUk9NIGV4dGVybmFsLnZpd19ncmVlbml0X3VuaWZpZWQiKQ0KDQojIGpvaW4gdGhlIGdyZWVuaXQgdGFibGUgd2l0aCBvdXIgc21wIGxpc3QNCiAgICBzbXBfZmVhdHVyZXMgPC0gY3dsX3NtcCAlPiUgDQogICAgICBpbm5lcl9qb2luKHNtcGJkdiwgYnk9InNtcF9pZCIpDQojIFVubW9uaXRvcmVkIFNNUHMNCiAgICB1bm1vbml0b3JlZF9zbXAgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcRmFyc2hhZC5FYnJhaGltaVxcT25lRHJpdmUgLSBDaXR5IG9mIFBoaWxhZGVscGhpYVxcR2l0aHViIFByb2plY3RzXFxzbXAtcG9wdWxhdGlvbi1hc3Nlc21lbnRcXHVubW9uaXRvcmVkX3NpdGVzLmNzdiIpICU+JQ0KICAgICAgc2VsZWN0KHNtcF9pZCA9IFNNUC5JRCkgJT4lDQogICAgICBkaXN0aW5jdCgpDQojIHVubW9uaXRvcmVkIHNtcF9mZWF0dXJlcw0KICAgIHVubW9uaXRvcmVkX3NtcF9mZWF0dXJlcyA8LSB1bm1vbml0b3JlZF9zbXAgJT4lDQogICAgICBpbm5lcl9qb2luKHNtcGJkdiwgYnk9InNtcF9pZCIpDQpgYGANCg0KVGhlIGNvZGUgY2FsY3VsYXRlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIFNNUCB0eXBlcyB3aXRoIENXTCBkYXRhIGluIHRoZSBNQVJTIGRhdGFiYXNlLCBwcm92aWRpbmcgYW4gaW5pdGlhbCB1bmRlcnN0YW5kaW5nIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgU01QIHN1YnR5cGVzLg0KDQpgYGB7ciBzZWN0aW9uIDIsIE1vbml0b3JlZCBTTVAgYnJlYWstZG93biBiYXNlZCBvbiB0eXBlfQ0KI251bWJlciBvZiBzbXAgdHlwZXMNCnNtcF90eXBlX2JyZWFrIDwtIHNtcF9mZWF0dXJlcyAlPiUgDQogIGdyb3VwX2J5KHNtcF9zbXB0eXBlKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgc2VsZWN0KFR5cGUgPSBzbXBfc21wdHlwZSwgY291bnQpDQpgYGANCg0KYGBge3Igc2VjdGlvbiAzIHBpZSBjaGFydCBvZiBtb25pdG9yZWQgU01QcywgZWNobz1GYWxzZX0NCmdncGxvdChzbXBfdHlwZV9icmVhaywgYWVzKHg9IiIsIHk9IGNvdW50LCBmaWxsPVR5cGUpKStnZW9tX2Jhcih3aWR0aCA9IDEsIHN0YXQgPSAiaWRlbnRpdHkiKStjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApDQpgYGANCg0KYGBge3Igc2VjdGlvbiA0LCBsb29raW5nIGF0IHRoZSB1bm1vbml0b3JlZCBTTVBzIHRvIHNlZSB0aGUgYnJlYWstZG93bn0NCiNudW1iZXIgb2YgdW5tb25pdG9yZWQgc21wcw0KdW5tb25pdG9yZWRfc21wX3R5cGVfYnJlYWsgPC0gdW5tb25pdG9yZWRfc21wX2ZlYXR1cmVzICU+JSANCiAgZ3JvdXBfYnkoc21wX3NtcHR5cGUpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBzZWxlY3QoVHlwZSA9IHNtcF9zbXB0eXBlLCBjb3VudCkNCmBgYA0KDQpTaW1pbGFyIGFuYWx5c2lzIGlzIHBlcmZvcm1lZCBiZWxvdyB0byBjYWxjdWxhdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBTTVAgdHlwZXMgaW4gdGhlIGVudGlyZSBwb3B1bGF0aW9uIG9mIFNNUCBpbmNsdWRpbmcgdGhlIG1vbml0b3JlZCBhbmQgdW5tb25pdG9yZWQgU01Qcy4NCg0KYGBge3Igc2V0aW9uIDUgcGllIGNoYXJ0IG9mIHVubW9uaXRvcmVkIFNNUHMsIGVjaG89RkFMU0V9DQpnZ3Bsb3QodW5tb25pdG9yZWRfc21wX3R5cGVfYnJlYWssIGFlcyh4PSIiLCB5PSBjb3VudCwgZmlsbD1UeXBlKSkrZ2VvbV9iYXIod2lkdGggPSAxLCBzdGF0ID0gImlkZW50aXR5IikrY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKQ0KYGBgDQoNClRoZSBhbmFseXNpcyBhaW1zIHRvIGRldGVybWluZSB0aGUgb3Zlci1yZXByZXNlbnRlZCAobm9ybWFsaXplZCBwZXJjZW50IFw+IDEpIGFuZCB1bmRlci1yZXByZXNlbnRlZCAobm9ybWFsaXplZCBwZXJjZW50IFw8IDEpIFNNUCB0eXBlcyBieSBkaXZpZGluZyB0aGUgZnJhY3Rpb24gb2YgYSBzcGVjaWZpYyBTTVAgdHlwZSBpbiB0aGUgbW9uaXRvcmVkIGRhdGEgYnkgdGhlIGZyYWN0aW9uIGluIHRoZSBlbnRpcmUgcG9wdWxhdGlvbi4NCg0KYGBge3Igc2VjdGlvbiA2IGZ1bGwgam9pbiB0aGUgdW5tb25pdG9yZWQgYW5kIG1vbml0b3JlZCwgZWNobz1GQUxTRX0NCiNmdWxsIGpvaW4gdGhlIHVubW9uaXRvcmVkIGFuZCBtb25pcm9yZWQgYW5kIGNyZWF0ZSBhIGNvbHVtbiB0byBzdW0gdGhlbSB1cA0Kc21wX2JyZWFrZHdvbl9mdWxsIDwtIHNtcF90eXBlX2JyZWFrICU+JQ0KICBmdWxsX2pvaW4odW5tb25pdG9yZWRfc21wX3R5cGVfYnJlYWssIGJ5PSJUeXBlIikgJT4lDQogIHNlbGVjdChUeXBlLCBjb3VudF9tb25pdG9yZWQgPSBjb3VudC54LCBjb3VudF91bm1vbml0b3JlZCA9IGNvdW50LnkpDQoNCiNyZXBsYWNlIG5hIHdpdGggemVybw0Kc21wX2JyZWFrZHdvbl9mdWxsW2lzLm5hKHNtcF9icmVha2R3b25fZnVsbCldIDwtIDANCg0KI211dGF0ZSB0aGUgdGhpcmQgY29sdW1uDQpzbXBfYnJlYWtkd29uX2Z1bGwgPC0gc21wX2JyZWFrZHdvbl9mdWxsICU+JQ0KICBtdXRhdGUoc3VtX2FsbCA9IGNvdW50X21vbml0b3JlZCArIGNvdW50X3VubW9uaXRvcmVkKQ0KDQojZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcgKG9ubHkgdHdvIGNvbHVtbnMpDQpwaXZvdF9kZiA8LSBzbXBfYnJlYWtkd29uX2Z1bGwgJT4lDQogIHNlbGVjdChUeXBlLCBjb3VudF9tb25pdG9yZWQsIHN1bV9hbGwpDQoNCmRmX2xvbmdlciA8LSBwaXZvdF9sb25nZXIocGl2b3RfZGYsIGNvbHMgPSBjKCJjb3VudF9tb25pdG9yZWQiLCAic3VtX2FsbCIpLCBuYW1lc190byA9ICJjYXRlZ29yeSIsIHZhbHVlc190byA9ICJjb3VudCIpDQoNCiNQbG90DQpnZ3Bsb3QoZGZfbG9uZ2VyLGFlcyh4ID0gVHlwZSwgeSA9IGNvdW50LCBmaWxsPSBjYXRlZ29yeSkpICsNCiAgICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSkrDQogICAgICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NiwgYW5nbGUgPSAzNSksDQogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApKQ0KDQpgYGANCg0KYGBge3Igc2VjdGlvbiA3LCBhZGRpbmcgdGhlIG1vbml0b3JlZCBhbmQgdW5tb25pb3JlZH0NCiNjb21iaW5pbmcgYWxsIHNtcHMgKG1vbml0b3JlZCBhbmQgdW5tb25pdG9yZWQpIGFuZCBnZXQgZnJhY3Rpb25zIGJhc2VkIG9uIHR5cGUgb2Ygc21wLWZpbmFsbHkgbm9ybWFsaXplIHRoZW0NCnRvdGFsX3NtcCA8LSB1bm1vbml0b3JlZF9zbXBfdHlwZV9icmVhayAlPiUNCiAgZnVsbF9qb2luKHNtcF90eXBlX2JyZWFrLCBieT0iVHlwZSIpDQp0b3RhbF9zbXBbaXMubmEodG90YWxfc21wKV0gPC0gMA0KdG90YWxfc21wIDwtIHRvdGFsX3NtcCAlPiUNCiAgbXV0YXRlKHRvdGFsX251bWJlciA9IGNvdW50LngrY291bnQueSkgJT4lDQogIHNlbGVjdChUeXBlLCB0b3RhbF9udW1iZXIpDQp0b3RhbF9zbXAgPC0gdG90YWxfc21wICU+JQ0KICAgIG11dGF0ZShzdW1fYWxsID0gc3VtKHRvdGFsX251bWJlcikpICU+JQ0KICAgIG11dGF0ZShwZXJjZW50X2FsbCA9ICh0b3RhbF9udW1iZXIvc3VtX2FsbCkqMTAwKQ0KDQojZ2V0IHRoZSBwZXJjZW50IG9mIG1vbml0b3JlZCBTTVANCnNtcF90eXBlX2JyZWFrX2ZyYWN0aW9uIDwtIHNtcF90eXBlX2JyZWFrICU+JQ0KICAgIG11dGF0ZShzdW1fYWxsID0gc3VtKGNvdW50KSkgJT4lDQogICAgbXV0YXRlKHBlcmNlbnRfbW9uaXRvcmVkID0gKGNvdW50L3N1bV9hbGwpKjEwMCkNCg0KI25vcm1hbGl6ZSB0aGUgbW9uaXRvcmVkIHNtcCBieSB0aGUgdG90YWwgc21wIHBlcmNlbnRzDQpub3JtYWxpemVkX2ZyYWN0aW9ucyA8LSBzbXBfdHlwZV9icmVha19mcmFjdGlvbiAlPiUNCiAgZnVsbF9qb2luKHRvdGFsX3NtcCwgYnk9IlR5cGUiKSAlPiUNCiAgbXV0YXRlKG5vcm1hbGl6ZWRfcGVyY2VudCA9IHBlcmNlbnRfbW9uaXRvcmVkL3BlcmNlbnRfYWxsKSAlPiUNCiAgc2VsZWN0KFR5cGUsIHBlcmNlbnRfbW9uaXRvcmVkLCBwZXJjZW50X2FsbCwgbm9ybWFsaXplZF9wZXJjZW50KQ0KDQpub3JtYWxpemVkX2ZyYWN0aW9uc1tpcy5uYShub3JtYWxpemVkX2ZyYWN0aW9ucyldIDwtIDANCmBgYA0KDQpgYGB7cn0NCiNjYWxjdWxhdGluZyB0aGUgbm9ybWFsaXplZCBwZXJjZW50YWdlIG9mIHNtcCB0eXBlcw0Ka2FibGUoYXJyYW5nZShub3JtYWxpemVkX2ZyYWN0aW9ucywgbm9ybWFsaXplZF9wZXJjZW50KSwgY2FwdGlvbiA9ICJOb3JtYWxpemVkIFNNUCBCcmVhay1kb3duIikNCmBgYA0KDQpUaGUgdGFibGUgYWJvdmUgc2hvd3MgVHJlZSB0cmVuY2hlcyBhcmUgb3Zlci1yZXByZXNlbnRlZCBpbiBvdXIgQ1dMIGRhdGEsIHdoaWxlIGJ1bXBvdXQsIHN3YWxlIGFuZCBwbGFudGVycyBhcmUgdW5kZXItcmVwcmVzZW50ZWQgKGR1ZSB0byBtb25pdG9yaW5nIGxpbWl0YXRpb24sIGV0YykuIEhvd2V2ZXIsIGl0IHNob3VsZCBiZSBub3RlZCB0aGF0IE1BUlMgZG9lcyBub3QgcGxhbiBvbiBtb25pdG9yaW5nIGdyZWVmIHJvb2ZzIGR1ZSB0byBsaW1pdGF0aW9ucyBvbiBhY2Nlc3MgdG8gdGhlc2Ugc3lzdGVtcyBhbmQgYSAxOjEgbG9hZGluZyByYXRpb3MuIFN0cm9td2F0ZXIgdHJlZXMsIHNtaWxhcmx5LCBkb2VzIG5vdCBoYXZlIGFueSBDV0wgZGF0YSBhbmQgdGhlIG1vbml0b3JpbmcgZWZmb3J0cyBhcmUgbGltaXRlZCB0byBDRVQgdGVzdHMgb25seS4gUG9yb3VzIHBhdmVtZW50cyBhcmUgYWxzbyBsaW1pdGVkIHRvIGluZmlsdHJhdGlvbiByYXRlIHRlc3RpbmcgYW5kIGhhdmUgbm8gQ1dMIGRhdGEuDQoNCjIuICAqKkNsdXN0ZXJpbmcgYXQgdGhlIHN5c3RlbS1sZXZlbCoqDQoNClRoZSBjdXJyZW50IHNlY3Rpb24gYWltcyB0byBjYXRlZ29yaXplIHRoZSBzeXN0ZW1zIHdpdGhpbiBlYWNoIGJyb2FkIHN1YnR5cGVzLiBUaGUgcmVhc29uIHRvIHBlcmZvcm0gdGhpcyBhdCB0aGUgc3lzdGVtIGxldmVsIGlzIHRoZSBhY2Nlc3NpYmlsaXR5IG9mIGRlc2lnbiBtZXRyaWNzIGF0IHRoZSBzeXN0ZW0gbGV2ZWwuIFRoZSBjbHVzdGVyaW5nIHdpdGhpbiBlYWNoIHR5cGUgb2Ygc3lzdGVtcyBpcyBkb25lIHRvIGVuc3VyZSB0aGUgc2FtcGxpbmcgYXBwcm9hY2ggcGlja3MgYW4gYXBwcm9wcmlhdGUgbnVtYmVyIG9mIHN5c3RlbXMgZnJvbSBzeXN0ZW1zIHBvc3Nlc3Npbmcgc2ltaWxhciBjaGFyYWN0ZXJpc3RpY3Mgd2l0aGluIHRoZXNlIHR5cGVzLiBGb3IgZXhhbXBsZSwgdGhlIHNhbXBsaW5nIG1ldGhvZCBzaG91bGQgc2VsZWN0IGEgbGFyZ2VyIG51bWJlciBvZiBCaW9pbmZpbHRyYXRpb24gc3lzdGVtcyB3aXRoIGhpZ2hlciBkcmFpbmFnZSBhcmVhIGlmIHRob3NlIHN5c3RlbXMgY29uc3RpdHV0ZSB0aGUgbWFqb3JpdHkgb2YgQmlvaW5maWx0cmF0aW9uIHN5c3RlbXMuDQoNClRoZSBiaW5uaW5nIGFwcHJvYWNoIGNvbnNpZGVycyBzZXZlcmFsIHN5c3RlbSBmZWF0dXJlcyBsaWtlIGxvYWRpbmcgcmF0aW9zLCBkcmFpbmFnZSBhcmVhLCBhbmQgc3Rvcm0gc2l6ZSBtYW5hZ2VtZW50LiBUaGUgY29kZSBjaHVua3MgYmVsb3cgZGVtb25zdHJhdGUgYSBjbHVzdGVyaW5nIG1ldGhvZCBhcHBsaWVkIHRvIHVubW9uaXRvcmVkIHN5c3RlbXMsIHJlZ2FyZGxlc3Mgb2Ygc3VidHlwZXMsIGFzIGFuIGV4YW1wbGUgb2YgaG93IGl0IGNhbiBncm91cCBzeXN0ZW1zIGJhc2VkIHNvbGVseSBvbiB0aGVzZSB0aHJlZSBkZXNpZ24gbWV0cmljcy4gVGhlIGNsdXN0ZXJpbmcgd2lsbCB0aGVuIGJlIHJlcGVhdGVkIHdpdGhpbiBlYWNoIHN1YnR5cGUgdG8gcHJvdmlkZSBhIG1vcmUgcmVsZXZhbnQgbWV0aG9kIGZvciBzYW1wbGluZy4NCg0KYGBge3Igc2VjdGlvbiA4LCByYXdzdG9ybXNpemVtYW5hZ2VkLWluICwgc3lzX2ltcGVydmRhX2Z0MiwgYW5kIGxvYWRpbmcgcmF0aW8gbWV0cmljcyBnYXRoZXJpbmd9DQojcmF3c3Rvcm1zaXplbWFuYWdlZC1pbiAsIHN5c19pbXBlcnZkYV9mdDIsIGFuZCBsb2FkaW5nIHJhdGlvIG1ldHJpY3MgZ2F0aGVyaW5nDQoNCnRhYmxlX2FsbCA8LSB1bm1vbml0b3JlZF9zbXBfZmVhdHVyZXMgJT4lDQogIGxlZnRfam9pbihzeXNiZHYsIGJ5PSJzeXN0ZW1faWQiKSAlPiUNCiAgbXV0YXRlKGxvYWRpbmdfcmF0aW8gPSBzeXNfbHJ0b3RhbGRhX2Z0MiApDQpgYGANCg0KSy1tZWFucyBjbHVzdGVyaW5nIGlzIGFuIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBtZXRob2QgdGhhdCBncm91cHMgZGF0YSBwb2ludHMgYmFzZWQgb24gdGhlaXIgcHJveGltaXR5IHRvIGNlbnRyb2lkcywgd2hpY2ggYXJlIGNhbGN1bGF0ZWQgYXMgdGhlIG1lYW4gb2YgYWxsIHBvaW50cyBpbiBhIGNsdXN0ZXIuIFRoZSBhbGdvcml0aG0gc3RhcnRzIGJ5IHJhbmRvbWx5IHNlbGVjdGluZyBpbml0aWFsIGNlbnRyb2lkcywgYW5kIHRoZW4gcmVwZWF0ZWRseSBhZGp1c3RzIHRoZW0gdW50aWwgY29udmVyZ2VuY2UgaXMgYWNoaWV2ZWQNCg0KVGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGlzIGRldGVybWluZWQgdXNpbmcgdGhlIEVsYm93IG1ldGhvZC4gSW4gc3VtbWFyeSwgdGhpcyBtZXRob2QgcGlja3MgdGhlIG51bWJlciBvZiBjbHVzdGVycyBpbiB3aGljaCBhIHRoZSBzbG9wZSBvZiB0aGUgbGluZXMgY2hhbmdlcyBtb3JlIGRyYXN0aWNhbGx5IChjbHVzdGVyIDQgaW4gdGhlIGZvbGxvd2luZyBncmFwaCkuDQoNCmBgYHtyIHNlY3Rpb24gOX0NCiNDbHVzdHJpbmcgYW5hbHlzaXMgYmFzZWQgb24gMyBmZWF0dXJlcyBvZiBkcmFpbmFnZSBhcmVhLCBsb2FkaW5nIHJhdGlvLCBhbmQgc3Rvcm0gc2l6ZWQgbWFuYWdlZA0KDQpjbHVzdGVyZWRfbW9uaW9yZWQgPC0gdGFibGVfYWxsICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBsb2FkaW5nX3JhdGlvLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbikgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfbW9uaW9yZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSA8LSBzY2FsZShjbHVzdGVyZWRfbW9uaW9yZWRbLDI6NF0pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBjZW50ZXJzID0gNCkNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9tb25pb3JlZCRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9tb25pb3JlZCRjbHVzdGVyKQ0KYGBgDQoNClRoZSBhYm92ZSB0YWJsZSBkaXNwbGF5cyB0aGUgbnVtYmVyIG9mIHN5c3RlbXMgaW4gZWFjaCBjbHVzdGVyLiBUaGUgc3Vic2VxdWVudCB0YWJsZXMgYWltIHRvIGRldGVybWluZSB0aGUgYXZlcmFnZSBvZiBzeXN0ZW0gZGVzaWduIG1ldHJpY3MgaW4gZWFjaCBjYXRlZ29yeS4gVGhlIGRhdGEgY2FuIGJlIGFuYWx5emVkIGJ5IGV4YW1pbmluZyBlYWNoIGNhdGVnb3J5OyBmb3IgaW5zdGFuY2UsIENsdXN0ZXIgRm91ciBlbmNvbXBhc3NlcyBzeXN0ZW1zIHdpdGggYSBsYXJnZSBkcmFpbmFnZSBhcmVhLCBjYXBhYmxlIG9mIGhhbmRsaW5nIGxhcmdlciBzdG9ybSBzaXplcyAoXD4zIGluY2gvaHIpLg0KDQpgYGB7ciBzZWN0aW9uIDEwIGFuYWx5emluZyB0aGUgY2x1c3RlciBmZWF0dXJlc30NCiMgR3JvdXAgYnkgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2VzDQpzeXNfY2x1c19hdmcgPC0gY2x1c3RlcmVkX21vbmlvcmVkICU+JQ0KICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICAgIHN1bW1hcml6ZV9pZihpcy5udW1lcmljLCBtZWFuKQ0KDQojIFZpZXcgdGhlIHJlc3VsdGluZyB0YWJsZQ0Ka2FibGUoc3lzX2NsdXNfYXZnKQ0KYGBgDQoNCmBgYHtyIDJEIFBsb3R9DQpnZ3Bsb3QoZGF0YSA9IGNsdXN0ZXJlZF9tb25pb3JlZCwgYWVzKHggPSBsb2FkaW5nX3JhdGlvLCB5ID0gc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIGNvbG9yID0gY2x1c3RlcikpICsNCiAgICBnZW9tX3BvaW50KCkNCmBgYA0KDQpgYGB7ciAzRCBwbG90fQ0KcGxvdF9seShjbHVzdGVyZWRfbW9uaW9yZWQsIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5sb2FkaW5nX3JhdGlvLCB6ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQpJbiBvcmRlciB0byBjcmVhdGUgYSBtb3JlIHBlcnRpbmVudCBjbHVzdGVyaW5nIG1ldGhvZCB0aGF0IGRpc3Rpbmd1aXNoZXMgYmV0d2VlbiBnZW5lcmFsIHR5cGVzIG9mIHN5c3RlbXMgKGxpbmVkIHZzLiB1bmxpbmVkIG9yIGJpb3JldGVudGlvbiB2cy4gYmlvaW5maWx0cmF0aW9uKSwgdGhlIHVubW9uaXRvcmVkIHN5c3RlbXMgd2VyZSBkaXZpZGVkIGludG8gYnJvYWQgY2F0ZWdvcmllcywgaW5jbHVkaW5nIEJpb2luZmlsdHJhdGlvbiwgQmlvcmV0ZW50aW9uIChsaW5lZCksIEJpb3JldGVudGlvbiAodW5saW5lZCksIFN1YnN1cmZhY2UgSW5maWx0cmF0aW9uLCBTdWJzdXJmYWNlIFNsb3cgUmVsZWFzZSAobGluZWQpLCBhbmQgU3Vic3VyZmFjZSBTbG93IFJlbGVhc2UgKHVubGluZWQpIHVzaW5nIHRoZSBzeXN0ZW0tbGV2ZWwgZmVhdHVyZSBjYWxsZWQgc3lzX21vZGVsaW5wdXRjYXRlZ29yeS4gVGhlIGNvZGUgY2h1bmsgYmVsb3cgZGlzcGxheXMgYSBicmVha2Rvd24gb2YgdGhlc2UgU01Qcy4gVGhlIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIHdhcyByZXBlYXRlZCBmb3IgZWFjaCBvZiB0aG9zZSBncm91cHMgc2ltaWxhcmx5LiBQbGVhc2Ugbm90ZSB0aGF0IEdyZWVuIFJvb2ZzIGFuZCBQZXJtZWFibGUgUGF2ZW1lbnRzIHdlcmUgbm90IGluY2x1ZGVkIGR1ZSB0byBhIGxvdyBudW1iZXIgb2Ygc3lzdGVtcy4NCg0KYGBge3Igc2VjdGlvbiAxMSBzeXNfbW9kZWxpbnB1dGNhdGVnb3J5IGlzIHVzZWQgdG8gY2F0ZWdvcml6ZSBzeXN0ZW1zfQ0KI3N5c19tb2RlbGlucHV0Y2F0ZWdvcnkgY2F0ZWdvcmllcw0Kc3lzX2NhdCA8LSB0YWJsZV9hbGwgJT4lDQogIHNlbGVjdChzeXN0ZW1faWQsIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpJT4lDQogIGRpc3RpbmN0ICU+JQ0KICBncm91cF9ieShzeXNfbW9kZWxpbnB1dGNhdGVnb3J5KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KDQprYWJsZShzeXNfY2F0KQ0KYGBgDQoNCioqLUJpb2luZmlsdHJhdGlvbiBDbHVzdGVyaW5nOioqDQoNCmBgYHtyIHNlY3Rpb24gMTIgY2x1c3RlcmluZyBCaW9pbmZpbHRyYXRpb24gdHlwZSBzeXN0ZW1zIH0NCg0KY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvbiA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJCaW9pbmZpbHRyYXRpb24iKSAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX2ltcGVydmRhX2Z0MiwgbG9hZGluZ19yYXRpbywgc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIGRpc3RpbmN0KCkNCg0KI25vcm1hbHplIHVzaW5nIG1lYW4tc2Qgc3RhbmRhcmRpemF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXIgPC0gY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvbg0Kbm9ybWFsaXplZF9jbHVzdGVyWywyOjRdIDwtIHNjYWxlKGNsdXN0ZXJlZF9CaW9pbmZpbHRyYXRpb25bLDI6NF0pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBjZW50ZXJzID0gNCkNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9CaW9pbmZpbHRyYXRpb24kY2x1c3RlciA8LWZhY3RvcihjbHVzdGVyX3NvbHV0aW9uJGNsdXN0ZXIpIA0KDQojIExvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzDQp0YWJsZShjbHVzdGVyZWRfQmlvaW5maWx0cmF0aW9uJGNsdXN0ZXIpDQoNCiMgR3JvdXAgYnkgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2VzDQpzeXNfY2x1c19hdmcgPC0gY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvbiAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9CaW9pbmZpbHRyYXRpb24sIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5sb2FkaW5nX3JhdGlvLCB6ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQoqKi1CaW9yZXRlbnRpb24gKGxpbmVkKSBjbHVzdGVyaW5nLiBOb3RlIHRoYXQgbG9hZGluZyByYXRpbyBmaWVsZCBpcyBub3QgYXBwbGljYWJsZSBoZXJlOioqDQoNCmBgYHtyIHNlY3Rpb24gMTMgQmlvcmV0ZW50aW9uIGxpbmVkIGNsdXN0ZXJpbmd9DQoNCmNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJCaW9yZXRlbnRpb24gKGxpbmVkKSIpICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgc3lzX21vZGVsaW5wdXRjYXRlZ29yeSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfQmlvcmV0ZW5fbGluZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSA8LSBzY2FsZShjbHVzdGVyZWRfQmlvcmV0ZW5fbGluZWRbLDI6M10pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjNdLCBjZW50ZXJzID0gMikNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCRjbHVzdGVyKQ0KDQojIEdyb3VwIGJ5IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlcw0Kc3lzX2NsdXNfYXZnIDwtIGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCwgeCA9IH5zeXNfaW1wZXJ2ZGFfZnQyLCB5ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQoqKi1CaW9yZXRlbnRpb24gKHVubGluZWQpIGNsdXN0ZXJpbmc6KioNCg0KYGBge3Igc2VjdGlvbiAxNCBCaW9yZXRlbnRpb24gdW5saW5lZCBjbHVzdGVyaW5nfQ0KDQpjbHVzdGVyZWRfQmlvcmV0ZW5fdW5saW5lZCA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJCaW9yZXRlbnRpb24gKHVubGluZWQpIikgJT4lDQogIHNlbGVjdChzeXN0ZW1faWQsIHN5c19pbXBlcnZkYV9mdDIsIHN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luLCBsb2FkaW5nX3JhdGlvLCBzeXNfbW9kZWxpbnB1dGNhdGVnb3J5KSAlPiUNCiAgbmEub21pdCgpICU+JQ0KICBkaXN0aW5jdCgpDQoNCiNub3JtYWx6ZSB1c2luZyBtZWFuLXNkIHN0YW5kYXJkaXphdGlvbg0Kbm9ybWFsaXplZF9jbHVzdGVyIDwtIGNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkDQpub3JtYWxpemVkX2NsdXN0ZXJbLDI6NF0gPC0gc2NhbGUoY2x1c3RlcmVkX0Jpb3JldGVuX3VubGluZWRbLDI6NF0pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBjZW50ZXJzID0gMykNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkJGNsdXN0ZXIgPC1mYWN0b3IoY2x1c3Rlcl9zb2x1dGlvbiRjbHVzdGVyKSANCg0KIyBMb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgY2x1c3RlciBhc3NpZ25tZW50cw0KdGFibGUoY2x1c3RlcmVkX0Jpb3JldGVuX3VubGluZWQkY2x1c3RlcikNCg0KIyBHcm91cCBieSB0aGUgY2x1c3RlciBhc3NpZ25tZW50IGFuZCBjYWxjdWxhdGUgYXZlcmFnZXMNCnN5c19jbHVzX2F2ZyA8LSBjbHVzdGVyZWRfQmlvcmV0ZW5fdW5saW5lZCAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkLCB4ID0gfnN5c19pbXBlcnZkYV9mdDIsIHkgPSB+c3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIHo9IH5sb2FkaW5nX3JhdGlvKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQoqKi1TdWJzdXJmYWNlIGluZmlsdHJhdGlvbiBjbHVzdGVyaW5nOioqDQoNCmBgYHtyIHNlY3Rpb24gMTUgU3Vic3VyZmFjZSBpbmZpbHRyYXRpb24gY2x1c3RlcmluZ30NCg0KY2x1c3RlcmVkX3N1YnN1cmZhY2UgPC0gdGFibGVfYWxsICU+JQ0KICBmaWx0ZXIoc3lzX21vZGVsaW5wdXRjYXRlZ29yeSA9PSAiU3Vic3VyZmFjZSBpbmZpbHRyYXRpb24iKSAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX2ltcGVydmRhX2Z0Miwgc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIGxvYWRpbmdfcmF0aW8sIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIGRpc3RpbmN0KCkNCg0KI25vcm1hbHplIHVzaW5nIG1lYW4tc2Qgc3RhbmRhcmRpemF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXIgPC0gY2x1c3RlcmVkX3N1YnN1cmZhY2UNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSA8LSBzY2FsZShjbHVzdGVyZWRfc3Vic3VyZmFjZVssMjo0XSkNCg0KI0VsYm93IG1ldGhvZCBzaG93cyBhIHN1YnRsZSBlbGJvdyBhdCBjbHVzdGVyIDQtaGVuY2UgNCBjbHVzdGVycyBpcyBvcHRpbXVtDQojbnVtYmVyIG9mIGNsdXN0ZXJzDQpmdml6X25iY2x1c3Qobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBrbWVhbnMsIG1ldGhvZCA9ICJ3c3MiKQ0KDQojay1tZWFucyANCiMgQ2x1c3RlciB1c2luZyBrbWVhbnMgd2l0aCBmaXZlIGNsdXN0ZXJzDQpjbHVzdGVyX3NvbHV0aW9uIDwtIGttZWFucyhub3JtYWxpemVkX2NsdXN0ZXJbLDI6NF0sIGNlbnRlcnMgPSAyKQ0KDQojIFN0b3JlIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnRzIGJhY2sgaW50byB0aGUgY2x1c3RlcmluZyBkYXRhIGZyYW1lIG9iamVjdA0KY2x1c3RlcmVkX3N1YnN1cmZhY2UkY2x1c3RlciA8LWZhY3RvcihjbHVzdGVyX3NvbHV0aW9uJGNsdXN0ZXIpIA0KDQojIExvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzDQp0YWJsZShjbHVzdGVyZWRfc3Vic3VyZmFjZSRjbHVzdGVyKQ0KDQojIEdyb3VwIGJ5IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlcw0Kc3lzX2NsdXNfYXZnIDwtIGNsdXN0ZXJlZF9zdWJzdXJmYWNlICU+JQ0KICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICAgIHN1bW1hcml6ZV9pZihpcy5udW1lcmljLCBtZWFuKQ0KDQojIFZpZXcgdGhlIHJlc3VsdGluZyB0YWJsZQ0Ka2FibGUoc3lzX2NsdXNfYXZnKQ0KDQojMyBEIHBsb3QNCnBsb3RfbHkoY2x1c3RlcmVkX3N1YnN1cmZhY2UsIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5zeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgej0gfmxvYWRpbmdfcmF0aW8pICU+JQ0KICBhZGRfbWFya2Vycyhjb2xvciA9IH5jbHVzdGVyKQ0KYGBgDQoNCioqLVN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlIChsaW5lZCkgY2x1c3RlcmluZzoqKg0KDQpgYGB7ciBzZWN0aW9uIDE2IFN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlIChsaW5lZCkJIGNsdXN0ZXJpbmd9DQoNCmNsdXN0ZXJlZF9zbG93cmVsX2xpbmVkIDwtIHRhYmxlX2FsbCAlPiUNCiAgZmlsdGVyKHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkgPT0gIlN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlIChsaW5lZCkiKSAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX2ltcGVydmRhX2Z0Miwgc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIGRpc3RpbmN0KCkNCg0KI25vcm1hbHplIHVzaW5nIG1lYW4tc2Qgc3RhbmRhcmRpemF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXIgPC0gY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSA8LSBzY2FsZShjbHVzdGVyZWRfc2xvd3JlbF9saW5lZFssMjozXSkNCg0KI0VsYm93IG1ldGhvZCBzaG93cyBhIHN1YnRsZSBlbGJvdyBhdCBjbHVzdGVyIDQtaGVuY2UgNCBjbHVzdGVycyBpcyBvcHRpbXVtDQojbnVtYmVyIG9mIGNsdXN0ZXJzDQpmdml6X25iY2x1c3Qobm9ybWFsaXplZF9jbHVzdGVyWywyOjNdLCBrbWVhbnMsIG1ldGhvZCA9ICJ3c3MiKQ0KDQojay1tZWFucyANCiMgQ2x1c3RlciB1c2luZyBrbWVhbnMgd2l0aCBmaXZlIGNsdXN0ZXJzDQpjbHVzdGVyX3NvbHV0aW9uIDwtIGttZWFucyhub3JtYWxpemVkX2NsdXN0ZXJbLDI6M10sIGNlbnRlcnMgPSAyKQ0KDQojIFN0b3JlIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnRzIGJhY2sgaW50byB0aGUgY2x1c3RlcmluZyBkYXRhIGZyYW1lIG9iamVjdA0KY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWQkY2x1c3RlciA8LWZhY3RvcihjbHVzdGVyX3NvbHV0aW9uJGNsdXN0ZXIpIA0KDQojIExvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzDQp0YWJsZShjbHVzdGVyZWRfc2xvd3JlbF9saW5lZCRjbHVzdGVyKQ0KDQojIEdyb3VwIGJ5IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlcw0Kc3lzX2NsdXNfYXZnIDwtIGNsdXN0ZXJlZF9zbG93cmVsX2xpbmVkICU+JQ0KICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICAgIHN1bW1hcml6ZV9pZihpcy5udW1lcmljLCBtZWFuKQ0KDQojIFZpZXcgdGhlIHJlc3VsdGluZyB0YWJsZQ0Ka2FibGUoc3lzX2NsdXNfYXZnKQ0KDQojMyBEIHBsb3QNCnBsb3RfbHkoY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWQsIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5zeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbikgJT4lDQogIGFkZF9tYXJrZXJzKGNvbG9yID0gfmNsdXN0ZXIpDQoNCg0KYGBgDQoNCioqLVN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlICh1bmxpbmVkKSBjbHVzdGVyaW5nOioqDQoNCmBgYHtyIHNlY3Rpb24gMTcgU3Vic3VyZmFjZSBzbG93IHJlbGVhc2UgKHVubGluZWQpCX0NCg0KY2x1c3RlcmVkX3Nsb3dyZWxfdW5saW5lZCA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJTdWJzdXJmYWNlIHNsb3cgcmVsZWFzZSAodW5saW5lZCkiKSAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX2ltcGVydmRhX2Z0Miwgc3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIGxvYWRpbmdfcmF0aW8sIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBuYS5vbWl0KCkgJT4lDQogIGRpc3RpbmN0KCkNCg0KI25vcm1hbHplIHVzaW5nIG1lYW4tc2Qgc3RhbmRhcmRpemF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXIgPC0gY2x1c3RlcmVkX3Nsb3dyZWxfdW5saW5lZA0Kbm9ybWFsaXplZF9jbHVzdGVyWywyOjNdIDwtIHNjYWxlKGNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWRbLDI6M10pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjNdLCBjZW50ZXJzID0gMikNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWQkY2x1c3RlciA8LWZhY3RvcihjbHVzdGVyX3NvbHV0aW9uJGNsdXN0ZXIpIA0KDQojIExvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzDQp0YWJsZShjbHVzdGVyZWRfc2xvd3JlbF91bmxpbmVkJGNsdXN0ZXIpDQoNCiMgR3JvdXAgYnkgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2VzDQpzeXNfY2x1c19hdmcgPC0gY2x1c3RlcmVkX3Nsb3dyZWxfdW5saW5lZCAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWQsIHggPSB+c3lzX2ltcGVydmRhX2Z0MiwgeSA9IH5zeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgeiA9IH5sb2FkaW5nX3JhdGlvKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQpVcG9uIGNsdXN0ZXJpbmcgdGhlIHN5c3RlbXMsIHRoZSBkYXRhIHdpbGwgYmUgY29sbGVjdGVkIGluIGEgdW5pZmllZCBkYXRhIGZyYW1lIGZvciBzdHJhdGlmaWVkIHNhbXBsaW5nLiBUaGUgc3RyYXRpZmllZCBzYW1wbGluZyBjcmVhdGVzIGEgYmFsYW5jZWQgbGlzdCBvZiBzeXN0ZW1zIHRoYXQgYXJlIHJlZmxlY3RpdmUgb2YgdGhlIHNpemUgb2YgZWFjaCBzeXN0ZW0gdHlwZSBhbmQgdGhhdCBvZiB0aGUgaW50ZXJuYWwgY2x1c3RlcnMuIEZvciBleGFtcGxlLCB0aGUgY29kZSBiZWxvdyBoYXMgZ2VuZXJhdGVkIDU4IHN5c3RlbSAoXH4gMTAlIG9mIHRvdGFsIHVubW9uaXRvcmVkIHN5c3RlbXMpOyB0aGUgc2l6ZSBvZiBlYWNoIHN5c3RlbSB0eXBlIGlzIHByb3BvcnRpb25hbCB0byB0aGF0IG9mIHRoZSBwb3B1bGF0aW9uLCBhbmQgd2l0aGluIGVhY2ggdHlwZSBvZiBzeXN0ZW1zLCBhIHByb3BvcnRpb25hbCBudW1iZXIgb2Ygc3lzdGVtcyBoYXMgYmVlbiBjaG9zZW4gZnJvbSBlYWNoIGNsdXN0ZXIgZS5nLiwgaW4gQmlvaW5maWx0cmF0aW9uIHR5cGUgc3lzdGVtcywgNyBzeXN0ZW1zIGFyZSByYW5kb21seSBjaG9zZW4gZnJvbSBjbHVzdGVyIDMsIDMgc3lzdGVtcyBmcm9tIGNsdXN0ZXIgMiwgYW5kIDIgc3lzdGVtcyBmcm9tIGNsdXN0ZXIgMS4NCg0KYGBge3Igc2VjdGlvbiAxOCBzYW1wbGluZ30NCiNiaW5kaW5nIGFsbCBzdWItdHlwZSBncm91cHMgaW50byBvbmUgdGFibGUNCnN5c19jbHVzdGVyZWRfYWxsIDwtIGJpbmRfcm93cyhjbHVzdGVyZWRfQmlvaW5maWx0cmF0aW9uWyxjKCJzeXN0ZW1faWQiLCJjbHVzdGVyIiwic3lzX21vZGVsaW5wdXRjYXRlZ29yeSIpXSwgY2x1c3RlcmVkX0Jpb3JldGVuX2xpbmVkWyxjKCJzeXN0ZW1faWQiLCJjbHVzdGVyIiwic3lzX21vZGVsaW5wdXRjYXRlZ29yeSIpXSwgY2x1c3RlcmVkX0Jpb3JldGVuX3VubGluZWRbLGMoInN5c3RlbV9pZCIsImNsdXN0ZXIiLCJzeXNfbW9kZWxpbnB1dGNhdGVnb3J5IildLCBjbHVzdGVyZWRfc2xvd3JlbF9saW5lZFssYygic3lzdGVtX2lkIiwiY2x1c3RlciIsInN5c19tb2RlbGlucHV0Y2F0ZWdvcnkiKV0sIGNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWRbLGMoInN5c3RlbV9pZCIsImNsdXN0ZXIiLCJzeXNfbW9kZWxpbnB1dGNhdGVnb3J5IildLCBjbHVzdGVyZWRfc3Vic3VyZmFjZVssYygic3lzdGVtX2lkIiwiY2x1c3RlciIsInN5c19tb2RlbGlucHV0Y2F0ZWdvcnkiKV0pDQoNCiNzYW1wbGluZyBjb2RlDQpzdHJhdGlmaWVkX3NhbXBsZSA8LSBzeXNfY2x1c3RlcmVkX2FsbCAlPiUNCiAgICBncm91cF9ieShzeXNfbW9kZWxpbnB1dGNhdGVnb3J5LCBjbHVzdGVyKSAlPiUNCiAgICBzYW1wbGVfZnJhYyhzaXplPTAuMSkNCg0Ka2FibGUoc3RyYXRpZmllZF9zYW1wbGUpDQpgYGANCg0KVG8gZ2VuZXJhdGUgbW9uaXRvcmluZyBzeXN0ZW1zLCB3ZSBkZXZlbG9wZWQgYSBzZW1pLXJhbmRvbSBhcHByb2FjaCB0aGF0IHV0aWxpemVzIGEgYmlubmluZyB0ZWNobmlxdWUuIFRoaXMgYXBwcm9hY2ggaW52b2x2ZXMgYXNzaWduaW5nIHN5c3RlbXMgdG8gY2x1c3RlcnMgYmFzZWQgb24gdGhlaXIgc3BlY2lmaWMgdHlwZSwgd2l0aCB0aGUgYmlubmluZyBwcm9jZXNzIHV0aWxpemluZyBhIGNvbWJpbmF0aW9uIG9mIGRlc2lnbiBtZXRyaWNzIHRvIGdyb3VwIHNpbWlsYXIgc3lzdGVtcyB0b2dldGhlci4gVG8gZW5zdXJlIGEgcmVwcmVzZW50YXRpdmUgc2FtcGxlIG9mIHN5c3RlbXMsIHRoZSByYW5kb20gc2VsZWN0aW9uIHByb2Nlc3MgY2hvb3NlcyBhIHByb3BvcnRpb25hbCBudW1iZXIgb2Ygc3lzdGVtcyBmcm9tIGVhY2ggY2x1c3RlciBhbmQgc3lzdGVtIHR5cGUgYmFzZWQgb24gdGhlaXIgc2l6ZS4NCg==